/** @file store_interface.h
 *
 * @brief Common store back end interfaces.
 *
 * Code using the store interface should include NOT this header, but rahter
 * `store.h`.
 *
 * This header is included by all back end implementations, which are in
 * their turn included by `store.h`.
 *
 * The basic interfaces for store and store iterator implementations are
 * defined here. New store implementations should include this header and
 * implement three basic elements:
 *
 * - A structure representing the store back end. This structure will be
 *   opaque to all downstream code and its layout is entirely up to the
 *   implementer.
 *
 * - A structure representing a store iterator state, also opaque.
 *
 * - The LSUP_StoreInt interface with all the functions defined in the
 *   interface necessary to interact with the store.
 *
 * See the `store_htable.{c,h}` and `store_mdb.{c,h}` files for examples of
 * fully functioning implementations.
 *
 * The #LSUP_StoreInt structure defines a store interface for raw buffer
 * triples. Nothing in the store functions' signatures should hint at RDF
 * triples—they should accept and produce exclusively raw byte buffers
 * (#LSUP_Buffer). A store interface may have any of the `LSUP_STORE_*` faeture
 * flags which should be reflected in the way its members are implemented.
 */


#ifndef _LSUP_STORE_INTERFACE_H
#define _LSUP_STORE_INTERFACE_H

#include "environment.h"

/*
 * Store feature flags.
 *
 * NOTE: LSUP_STORE_PERM need only be set by an implementation based on whether
 * its path is on a default temporary dir (e.g. LSUP_MDB_RAMDISK_PATH). If this
 * flag is not set, it means the data will be cleared before the next execution
 * of the program. However, its being set does not guarantee the persistence of
 * the medium (i.e. a "permanent" store may have been created ad hoc on a
 * tempfs).
 */
typedef enum {
    LSUP_STORE_PERM     = 1<<0,   ///< Store is on a permanent support.
    LSUP_STORE_CTX      = 1<<1,   ///< Store supports contexts (quads).
    LSUP_STORE_IDX      = 1<<2,   ///< Store is fully SPO(C)-indexed.
    LSUP_STORE_TXN      = 1<<3,   ///< Supports transaction handling.
    LSUP_STORE_COW      = 1<<4,   ///< Copy on write. @sa #iter_next_fn_t()
    //LSUP_STORE_NET      = 1<<5,   ///< Store is over a network protocol.
} LSUP_StoreFeature;


/*
 * Store function types.
 */

/** @brief Prototype: create any environment necessary for the store to work.
 *
 * This function should be idempotent on separate calls to the same `id`,
 * unless the `clear` option is set to `true`.
 *
 * @param[in,out] id Identifier to use for the store. This should be
 * a URI that uniquely identifies a back end for the implementation using it,
 * e.g. a SQL connection string, file path for an embedded store, the URL of a
 * REST API endpoint, etc. It may also be NULL, in which case it will be set to
 * the default identifier set by the implementation. It can be retrieved from
 * an existing store via #store_id_fn_t .
 *
 * @param[in] clear Whether to remove an existing environment with the same ID.
 */
typedef LSUP_rc (*store_setup_fn_t)(const char *id, bool clear);


/** @brief Prototype: create a new store.
 *
 * @param[in] id Identifier for the new store. How this is interpreted, and
 * whether it is even used, depends on the implementation, which should
 * provide documentation on how to pass and interpret this parameter.
 *
 * @param[in] size Initial size for the store. It may be 0. Only meaningful
 *  for stores that may preallocate memory, such as #HTStore.
 *
 * @return New store handle.
 */
typedef void * (*store_new_fn_t)(const char *id, size_t size);


/** @brief Prototype: free store handle.
 *
 * @param[in] store Store handle.
 *
 */
typedef void (*store_free_fn_t)(void *store);


/** @brief Prototype: get the store ID.
 *
 * @param[in] store  Store handle.
 *
 * @return store ID string. This is a copy and should be freed after use.
 */
typedef char * (*store_id_fn_t)(const void *store);


/** @brief Prototype: get store size.
 *
 * @param[in] store  The store to calculate size of.
 *
 * @return Number of stored SPO triples (across all contexts if supported).
 */
typedef size_t (*store_size_fn_t)(const void *store);


#if 0
/** @brief Print stats about a store.
 *
 * TODO
 *
 * @param[in] store The store to get stats for.
 */
typedef LSUP_rc (*store_stat_fn_t)(void *store, void *stat);
#endif


/** @brief Begin a transaction.
 *
 * Only for LSUP_STORE_TXN stores.
 *
 * The transaction handle is managed by the store implementation and can be any
 * data type.
 *
 * @param[in] store Store handle.
 *
 * @param[in] flags Transaction flags. These vary with each implementation.
 *
 * @param[out] txn Will point to the new open transaction on success, or to
 * undefined content on failure.
 *
 * @return LSUP_OK if the transaction started successfully, <0 on error.
 */
typedef LSUP_rc (*store_txn_begin_fn_t)(void *store, int flags, void **txn);


/** @brief Commit a transaction.
 *
 * Only for LSUP_STORE_TXN stores.
 *
 * @param[in] store Store handle.
 *
 * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t.
 *
 * @return LSUP_OK if the transaction was committed successfully, <0 on error.
 */
typedef LSUP_rc (*store_txn_commit_fn_t)(void *store);


/** @brief Abort a transaction.
 *
 * Only for LSUP_STORE_TXN stores.
 *
 * @param[in] store Store handle.
 *
 * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t.
 */
typedef void (*store_txn_abort_fn_t)(void *store);


/** @brief Initialize bulk triple load.
 *
 * This is the first step of a bulk load. It is best used when the data at hand
 * need to be pre-processed, which can be done in the same loop as the next
 * step to keep memory usage low.
 *
 * @param store[in] The store to add to.
 *
 * @param sc[in] Context as a serialized term. If this is NULL, and the
 *  default context is not NULL, triples will be added to the default context
 *  for the store, If the default context for the store is NULL, regardless of
 *  the value of sc, triples will be added with no context. Only meaningful
 *  for stores with the LSUP_STORE_CTX feature.
 *
 *  @param[in] udata User data. Consult individual store implementations for
 *   how this is interpreted.
 *
 * @return Iterator handle to be passed to the following load steps.
 */
typedef void * (*store_add_init_fn_t)(
        void *store, const LSUP_Buffer *sc, void *udata);


/** @brief Add one triple into the store.
 *
 * This must be called after #add_init_fn, using the iterator
 * yielded by that function. It may be called multiple times and must be
 * followed by #add_done_fn or #add_abort_fn (if supported).
 *
 * @param it[in] Iterator obtained by #store_add_init_fn_t.
 *  The following members are of interest:
 *  it->i stores the total number of records inserted.
 *
 * @param sspo[in] Serialized triple to be added.
 *
 * @return LSUP_OK if the triple was inserted; LSUP_NOACTION if the triple
 *  already existed; LSUP_DB_ERR if an MDB error occurred.
 */
typedef LSUP_rc (*store_add_iter_fn_t)(
        void *it, const LSUP_BufferTriple * sspo);


/** @brief Abort an add loop and free iterator.
 *
 * Usually called on an irrecoverable error from #add_iter_fn. None of the
 * successful inserts in the same loop is retained.
 *
 * @param it[in] Iterator obtained by #store_add_init_fn_t.
 */
typedef void (*store_add_abort_fn_t)(void *it);


/*
 * Iterator function types.
 */

/** @brief Get iterator active transaction handle.
 *
 * This function is used to get an active transaction during an iteration loop
 * in order to perform an action using the store state within that loop. Some
 * stores (e.g. MDB) only support one R/W open transaction per thread, so this
 * is also the only way to perform anything else than iterating or committing
 * while a loop is open.
 *
 * @param[in] it Iterator handle to get the transaction from.
 *
 * @return Transaction handle. DO NOT close this transaction directly.
 */
typedef void * (*iter_txn_fn_t)(void *it);


/** @brief Finalize an add loop and free iterator.
 *
 * This must be called after #add_iter_fn.
 *
 * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
 */
typedef LSUP_rc (*store_add_done_fn_t)(void *it);


/** @brief Add a single term to the store.
 *
 * @param[in] store Store handle.
 *
 * @param[in] sterm Serialized term to store.
 */
typedef LSUP_rc (*store_add_term_fn_t)(
        void *store, const LSUP_Buffer *sterm, void *udata);


/** @brief Prototype: look up triples by pattern matching.
 *
 * This function may return a count of matches and/or an iterator of results as
 * serialized triples.
 *
 * For stores with #LSUP_STORE_TXN, this opens a read-only transaction. The
 * transaction handle is held in the iterator structure and is closed when the
 * iterator is freed with #iter_free_fn_t().
 *
 * Any and all of the terms may be NULL, which indicates an unbound query
 * term. Stores witout context support will always ignore sc.
 *
 * @param[in] store The store to be queried.
 *
 * @param[in] ss Serialized s term.
 *
 * @param[in] sp Serialized p term.
 *
 * @param[in] so Serialized o term.
 *
 * @param[in] sc Serialized context to limit search to. It may be NULL, in
 * which case search is done in all contexts. Note that triples inserted
 * without context are assigned the *default* context for the store.
 *
 * @param[out] ct If not NULL, this will be populated with the number of
 *  entries found. It is very inexpensive to set for lookups without context,
 *  much less so for 1-bound and 2-bound context lookups, in which cases it
 *  should be set only if needed.
 *
 *  @param[in] udata User data. Consult individual store implementations for
 *   how this is interpreted.
 *
 * @return Iterator handle that will be populated with a result iterator. This
 * is always created even if no matches are found and must be freed with
 * #LSUP_mdbiter_free() after use. If matches are found, the iterator points to
 * the first result which can be retrieved with #iter_next_fn().
 */
typedef void * (*store_lookup_fn_t)(
        void *store,
        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
        const LSUP_Buffer *sc, void *udata, size_t *ct);


/** @brief Prototype: check for existence of a triple (T/F).
 *
 * @param[in] Store to be queried.
 *
 * @param[in] spo Triple to look up. All members must not be NULL.
 *
 * @param[in] c Optional context to look into. It may be NULL. It is
 *  disregarded by stores without the LSUP_STORE_CTX feature.
 *
 * @return Whether the triple exist in the store (/context).
 */
typedef bool (*store_trp_exist_fn_t)(
        void *store, const LSUP_BufferTriple *sspo, const LSUP_Buffer *sc);


/** @brief Prototype: delete triples by pattern matching.
 *
 * The ss, sp, so, sc terms act as a matching pattern as documented in
 * @sa #store_lookup_fn. if not NULL, ct yields the number of triples actually
 * deleted.
 *
 *  @param[in] udata User data. Consult individual store implementations for
 *   how this is interpreted.
 */
typedef LSUP_rc (*store_remove_fn_t)(
        void *store,
        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
        const LSUP_Buffer *sc, void *udata, size_t *ct);


/** @brief Put an in-memory namespace map into a permanent back end.
 *
 * This is only available in stores with the LSUP_STORE_PERM feature.
 *
 * Existing prefixes and namespaces are not updated. Thus, if the following are
 * already stored:
 *
 * ns1: <urn:ns:a#>
 * ns2: <urn:ns:b#>
 *
 * Neither of the following will be inserted:
 *
 * ns3: <urn:ns:a#>
 * ns2: <urn:ns:c#>
 *
 * @param[in] store MDB store to update.
 *
 * @param[out] nsm Namespace map handle to store.
 *
 * @param[in] udata User-defined data. Consult individual implementations for
 *  details.
 *
 * @return LSUP_OK if all terms were updated; LSUP_CONFLICT if one or more
 *  namespaces or terms were not updated because they already existed; <0 if
 *  an error occurred.
 */
typedef LSUP_rc (*store_nsm_put_fn_t)(
        void *store, const LSUP_NSMap * nsm, void *udata);


/** @brief Get the store's namespace prefix map.
 *
 * @param[in] store MDB store to query.
 *
 * @return NS map or NULL on error.
 */
typedef LSUP_NSMap * (*store_nsm_get_fn_t)(void *store);


/** @brief Prototype: yield the matching triples and advance the iterator.
 *
 * NOTE: Iterators keep transactions open. Don't hold on to them longer than
 * necessary.
 *
 * NOTE: If the store interface has the LSUP_STORE_COW feature, the memory
 * buffer referenced by the #LSUP_Buffer handle is owned by the database. It
 * must not be written to or freed. To modify the data or use them beyond the
 * caller's scope, this memory must be copied. Note that the #LSUP_Buffer
 * handle must still be freed (with a plain `free()`), but not the underlying
 * data buffer, since only the latter is owned by the back end. For stores
 * without the LSUP_STORE_COW, data are copied on retrieval and the resulting
 * buffers can be freed with #LSUP_buffer_free() or analogous methods.
 *
 * @param[in] it Opaque iterator handle obtained with the store's #lookup_fn.
 *
 * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
 * terms if found. It may be NULL. NOTE: the content of this variable is
 * undefined on rc != LSUP_OK.
 *
 * @param[out] ctx If not NULL, it is populated with a NULL-terminated array of
 *  LSUP_Buffer structs, one for each context associated with the matching
 *  triple. These contexts are the same regardless of the context filter used
 *  in the lookup. The array is freed with a simple #free(). This parameter
 *  is ignored by implementations without the LSUP_STORE_CTX feature.
 *
 *  To iterate over the context array, use this loop:
 *
 *      size_t i = 0;
 *      while (ctx[i].addr)
 *          do_something(ctx + (i++));
 *
 * @return LSUP_OK if results were found; LSUP_END if no (more) results were
 * found; LSUP_DB_ERR if a backend error occurred.
 */
typedef LSUP_rc (*iter_next_fn_t)(
        void *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx);


/** @brief Prototype: free an iterator allocated by a lookup.
 *
 * @param it[in] Iterator pointer. It will be set to NULL after freeing.
 */
typedef void (*iter_free_fn_t)(void * it);


/*
 * Iterface type definitions.
 */

/** @brief Store interface.
 *
 * New store implementations should define a static structure with the relevant
 * members filled in. Some members are only relevant to certain types of stores
 * and may be set to NULL.
 *
 * #setup_fn may be optionally defined and MUST cause an idempotent action,
 * unless the `clear` argument is set to `true`. Callers should check if this
 * member is NULL and if it is not, call it at the beginning of the
 * interaction with the store.
 *
 * Transaction control members are only applicable to stores with the
 * #LSUP_STORE_TXN feature.
 */
typedef struct store_if_t {
    // Basic properties.
    char                name[16];       ///< Store type name.
    LSUP_StoreFeature   features;       ///< Feature flags.

    // Allocation, setup and deallocation.
    store_setup_fn_t    setup_fn;       ///< Called before #store_new_fn_t.
    store_new_fn_t      new_fn;         ///< Create a new store instance.
    store_free_fn_t     free_fn;        ///< Free the store.

    // Metadata.
    store_size_fn_t     size_fn;        ///< Number of triples in the store.
    store_id_fn_t       id_fn;          ///< Get store ID.

    // Transaction control.
    store_txn_begin_fn_t txn_begin_fn;  ///< Begin transaction.
    store_txn_commit_fn_t txn_commit_fn; ///< Commit transaction.
    store_txn_abort_fn_t txn_abort_fn;  ///< Abort transaction.
    iter_txn_fn_t       iter_txn_fn;    ///< Get iterator's transaction.

    // Addition.
    store_add_init_fn_t add_init_fn;    ///< Initialize add iteration.
    store_add_iter_fn_t add_iter_fn;    ///< Add one triple.
    store_add_abort_fn_t add_abort_fn;  /**< Abort (roll back) the add process.
                                         *
                                         *   Only available in
                                         *   stores with #LSUP_STORE_TXN
                                         *   feature. Optional.
                                         */
    store_add_done_fn_t add_done_fn;    ///< Complete the add process.
    store_add_term_fn_t add_term_fn;    /**< Add (index) a term to the store.
                                         *
                                         *   Only available in stores with
                                         *   #LSUP_STORE_IDX feature. Optional.
                                         */

    // Look up.
    store_lookup_fn_t   lookup_fn;      ///< Look up triples by pattern. 
    //store_trp_exist_fn_t exist_fn;      ///< Check if a triple exists.
    iter_next_fn_t      lu_next_fn;     ///< Advance the lookup iterator.
    iter_free_fn_t      lu_free_fn;     ///< Free the lookup iterator.

    // Removal.
    store_remove_fn_t   remove_fn;      ///< Remove triples by pattern.

    // Namespace prefix mapping.
    store_nsm_put_fn_t  nsm_put_fn;     /**< Add a ns/pfx pair to the map.
                                         *
                                         *  Only available (and mandatory)
                                         *  in stores with the
                                         *  #LSUP_STORE_IDX feature.
                                         */
    store_nsm_get_fn_t  nsm_get_fn;     /**< Get a namespace from the map.
                                         *
                                         *  Only available (and mandatory)
                                         *  in stores with the
                                         *  #LSUP_STORE_IDX feature.
                                         */
} LSUP_StoreInt;


/*
 * Template for a new store and iterator implementation.
 * These should be placed in the .c file where the interface functions are
 * defined, and declared as `extern` in the related .h file.

const LSUP_StoreInt my_store_int = {
    .name           = "My Store", // Truncated to 15 chars.
    .features       = LSUP_STORE_PERM | LSUP_STORE_IDX,

    .setup_fn       = my_setup_fn,
    .new_fn         = my_new_fn,
    .free_fn        = my_free_fn,

    .size_fn        = my_size_fn,
    .id_fn          = my_id_fn,

    .txn_begin_fn   = my_txn_begin_fn,
    .txn_commit_fn  = my_txn_commit_fn,
    .txn_abort_fn   = my_txn_abort_fn,

    .add_init_fn    = my_init_fn,
    .add_iter_fn    = my_iter_fn,
    .add_abort_fn   = my_add_abort_fn,
    .add_done_fn    = my_add_done_fn,
    .add_term_fn    = my_add_term_fn,

    .lookup_fn      = my_lookup_fn,
    .lu_next_fn     = my_iter_next_fn,
    .lu_free_fn     = my_iter_free_fn,

    .remove_fn      = my_remove_fn,

    .nsm_put_fn     = my_nsm_put_fn,
    .nsm_get_fn     = my_nsm_get_fn,
};
*/

#endif  /* _LSUP_STORE_INTERFACE_H */
